/*
 * Copyright (c) 2013 Eugene Kuzmenko
 *
 * Permission  is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in  the Software without restriction, including without limitation the rights
 * to  use,  copy,  modify,  merge, publish, distribute, sublicense, and/or sell
 * copies  of  the  Software,  and  to  permit  persons  to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The  above  copyright  notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE  IS  PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED,  INCLUDING  BUT  NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS  FOR  A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS  OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR  ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * For more information, see <http://www.aljey.com>.
 */

//=../Button/Button

(function () {

/**
 * Stores a collection of all currently open windows.
 *
 * @type {Window[]}
 * @private
 */
var windows = [],

  /**
   * The largest z-index value among all windows.
   *
   * @type {number}
   * @private
   */
  index = 1050,

  /**
   * Shared overlay.
   *
   * @type {jQuery}
   * @private
   */
  overlay;

/**
 * Shows an overlay underneath a window.
 *
 * @param {Window} win
 * @private
 */
function showOverlay(win) {
  overlay || (overlay = $('<div class="modal-backdrop fade in"></div>')
    .appendTo(document.body));
  overlay.css('z-index', win.index - 1);
  overlay.show();
  overlay.off('click').on('click', function () { win.animate('shake') });
}

/**
 * Reassigns the overlay to the nearest available target, or hides it if it
 * can't find one.
 *
 * @param {Window} win
 * @private
 */
function hideOverlay(win) {
  var i = windows.length;
  while (i--) {
    if ((win !== windows[i]) && windows[i].modal) {
      showOverlay(windows[i]);
      return;
    }
  }
  overlay && overlay.hide();
}

/**
 * The basic window.
 *
 * @author Eugene Kuzmenko <mail@aljey.com>
 * @class {Window}
 * @extends {Container}
 */
Class.define('Window', {

  extend: 'Container',

  /**
   * The title of a window.
   *
   * @property {string}
   */
  title: '&nbsp;',

  /**
   * Show a black overlay.
   *
   * @property {bool}
   */
  modal: false,

  /**
   * A window size.
   *
   * @property {number}
   * @default 1
   * @example
   *   0 = small
   *   1 = default
   *   2 = large
   */
  size: 1,

  /** @inheritDoc */
  slots: [{
    name: 'items',
    renderTo: '.modal-body'
  }, {
    name: 'buttons',
    renderTo: '.modal-footer',
    defaults: {type: 'Button'}
  }],

  tpl: [
    '<%var title_id=uid()%>',
    '<div class="modal-dialog modal-content fade in',
        ' <%=(["modal-sm","","modal-lg"])[size]%>" role="dialog"',
        ' aria-labelledby="<%=title_id%>">',
      '<div class="modal-header">',
        '<button type="button" class="close">&times;</button>',
        '<h4 class="modal-title" id="<%=title_id%>"><%=title%></h4>',
      '</div>',
      '<%=runTemplate("bodyTpl")%>',
      '<div class="modal-footer"></div>',
    '</div>'
  ],

  bodyTpl: '<div class="modal-body"></div>',

  /**
   * Changes window title.
   *
   * @param {String} val
   * @returns {Window}
   */
  setTitle: _static.Container.setter(function (val) {
    this._textNode.nodeValue = val;
  }, 'title', 'string'),

  /**
   * Changes modality of the window.
   *
   * @param {bool} val
   * @returns {Window}
   */
  setModal: _static.Container.setter(function (val) {
    if (-1 !== _.indexOf(windows, this)) {
      if (overlay && overlay.hasClass('in')) {
        var index = parseInt(overlay.css('z-index'), 10);
        if (index < this.index) {
          if ((index + 1) === this.index) {
            val ? showOverlay(this) : hideOverlay(this);
          } else if (val) showOverlay(this);
        }
      } else if (val) showOverlay(this);
    }
  }, 'modal', 'bool'),

  /**
   * Changes the window size.
   *
   * @param {number} val A button size.
   * @returns {Window}
   */
  setSize: _static.Container.setter(function (val) {
    var sizes = ['modal-sm', '', 'modal-lg'];
    sizes[this.size] && this.el.removeClass(sizes[this.size]);
    sizes[val] && this.el.addClass(sizes[val]);
  }, 'size', 'number', 1),

  /**
   * Brings the window to front.
   *
   * @returns {Window}
   */
  bringToFront: function () {
    if (this.index !== index) {
      index += 2;
      this.index = index;
      this.el.css('z-index', index);
    }
    var i = _.indexOf(windows, this), j = windows.length - 1;
    this.modal && showOverlay(this);
    if ((i !== j) || (-1 === j)) {
      (-1 === i) || windows.splice(i, 1);
      windows.push(this);
    }
    return this;
  },

  listeners: {
    render: function () {
      var me = this, w = $(window), pad = windows.length * 10 + 10;
      me.el.css({top: pad + w.scrollTop(), left: pad + w.scrollLeft()});
      me._textNode = me.el.find('.modal-title').contents()[0];
      me.el.draggable({handle: '.modal-header'});
      me.el.mousedown(function () { me.bringToFront() });
      me.el.find('.close').click(function () { me.hide() });
      w.off('keyup').keyup(function (e) {
        var i = windows.length;
        if (i) {
          if (27 === e.which) {
            windows[i - 1].hide();
          } else if (13 === e.which) {
            windows[i - 1].fire('enter');
          }
        }
      });
    },
    show: function () {
      var w = $(window), pad = windows.length * 10 + 10,
        offset = this.el.offset(), top = w.scrollTop(), left = w.scrollLeft();
      (offset.top < top) && this.el.css('top', pad + top);
      (offset.left < left) && this.el.css('left', pad + left);
      this.bringToFront();
    },
    hide: function () {
      var i = _.indexOf(windows, this);
      (-1 === i) || windows.splice(i, 1);
      hideOverlay(this);
    },
    destroy: function () {
      this.hide();
    }
  }

});

}());